package view
{
	import events.ParaEvent;
	
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.events.MouseEvent;
	import flash.geom.Point;
	import flash.text.TextField;
	import flash.utils.Dictionary;
	
	import mx.core.UIComponent;
	
	import utils.FilterUtil;

	/**
	 *	根据传入的数组绘制链表 
	 * @author Roland
	 * 
	 */
	public class ChainViewer extends UIComponent
	{
		private var _data:Array = [{ start: 1, end: 2 }, { start: 2, end: 3 }, { start: 3, end: 4 }, { start: 4, end: 5 }, { start: 5, end: 6 }, { start: 9, end: 10 }, { start: 11, end: 12 }, { start: 13, end: 24 }, { start: 1, end: 2 }, { start: 2, end: 5 }];

		/**
		 *	网格宽度 
		 */
		private static const GRID_WIDTH:Number = 100;

		/**
		 * 网格高度 
		 */
		private static const GRID_HEIGTH:Number = 100;

		/**
		 * 起点/终点半径 
		 */
		private static var POINT_RADIUS:Number = 20;
//		private var _nodeMap:Vector.<Vector.<Object>>;

		/**
		 * 所有起点坐标字典
		 */
		private var _startedDic:Dictionary;
		/**
		 *	所有终点坐标字典 
		 */
		private var _endedDic:Dictionary;
		/**
		 *	已使用的网格节点字典 
		 */
		private var _nodeDic:Dictionary;
		/**
		 *	长循环链字典,链中节点至少为2,索引值为循环的起点(终点)Uid,项值为起点(终点)坐标
		 */
		private var _longLoopChainDic:Dictionary;
		/**
		 *	单循环链字典,即start==end的节点,索引值为循环的起点(终点)Uid
		 */
		private var _singleLoopChainDic:Dictionary;
		/**
		 *	链头的Uid列表 
		 */
		private var _headList:Vector.<String>;
		private var _pointBoard:Sprite;
		private var _lineBoard:Shape;
		public function ChainViewer()
		{
			super();
		}
		
		/**
		 *	 设置数据
		 *  格式示例:  [{ start: uid1, end: uid2 ,startIsTop:true }, { start: uid2, end: uid3,startIsTop:false },...]
		 * @param value
		 * 
		 */
		public function set data(value:Array):void
		{
			_data = value;
			startBuild();
		}
		
		public function get data():Array
		{
			return _data;
		}
		
		 private function startBuild():void
		 {
			 initData();
			 cleanUpDisplay();
			 buildPath();
		 }
		 
		 private function initData():void
		 {
			 _startedDic = new Dictionary();
			 _endedDic = new Dictionary();
			 _nodeDic = new Dictionary();
			 _longLoopChainDic = new Dictionary();
			 _singleLoopChainDic = new Dictionary();
			 _headList = new Vector.<String>;
		 }
		 
		 private function cleanUpDisplay():void
		 {
			 removeAllChildren();
			 
			 _lineBoard = new Shape();
			 _pointBoard = new Sprite();
			 addChild(_lineBoard);
			 addChild(_pointBoard);
		 }
		 
		 /**
		  * 删除所有子内容
		  */
		 public function removeAllChildren():void	{
			 while (numChildren > 0)	{
				 removeChildAt(0);
			 }
		 }

		private function buildPath():void
		{
			//画点和线,给终点加文本
			var $sp:Point;
			var $ep:Point;
			var $sUid:String;
			var $eUid:String;
			var $sIsTop:Boolean;
			for(var i:int=0;i<_data.length;i++)
			{
				$sUid = _data[i].start;
				$eUid =  _data[i].end;
				$sIsTop = _data[i].startIsTop;
				$sp = createStartPoint($sUid,$sIsTop);
				if(_data[i].hasOwnProperty("end"))
				{
					$ep = createEndPoint($sUid,$eUid);
				}else{//链尾
					$ep = $sp.clone();
				}
				drawLongLoopChain();
				drawSingleLoopChain();
				drawArrow($sp,$ep);
				addTxt($eUid,$ep);
			}
			//给起点加文本
			for(var j:int=0;j<_headList.length;j++)
			{
				var $uid:String = _headList[j];
				addTxt($uid,_startedDic[$uid]);
			}
			
			//标记循环链的起点/终点
//			for(var uid:String in _loopChainDic)
//			{
//				
//			}
		}
		
		
		private function addTxt(pointUid:String,point:Point):void
		{
			var txt:TextField = new TextField();
			txt.text = pointUid ? pointUid : "";
			txt.height = txt.textHeight+5;
			txt.width = txt.textWidth+5;
			txt.selectable = false;
			txt.textColor = 0xffffff;
			txt.x = point.x -txt.width/2;
			txt.y = point.y - txt.height/2;
			_pointBoard.addChild(txt);
			
			txt.addEventListener(MouseEvent.MOUSE_OVER,uidTxtOverHandler);
			txt.addEventListener(MouseEvent.MOUSE_OUT,uidTxtOutHandler);
		}
		
		private function uidTxtOutHandler(e:MouseEvent):void
		{
			var txt:TextField = e.target as TextField;
			txt.textColor = 0xffffff;
			txt.background = false;
			txt.border = false;
			var point:Point = new Point(txt.x,txt.y);
			point = localToGlobal(point);
			dispatchEvent(new ParaEvent(MouseEvent.MOUSE_OUT,{uid:txt.text,x:point.x,y:point.y}));
		}
		
		private function uidTxtOverHandler(e:MouseEvent):void
		{
			var txt:TextField = e.target as TextField;
			txt.textColor = 0x000000;
			txt.background = true;
			txt.backgroundColor = 0xffffff;
			txt.border = true;
			txt.borderColor = 0x000000;
			var point:Point = new Point(txt.x,txt.y);
			point = localToGlobal(point);
			dispatchEvent(new ParaEvent(MouseEvent.MOUSE_OVER,{uid:txt.text,x:point.x,y:point.y}));
		}
		
		/**
		 *	画长循环链 
		 */
		private function drawLongLoopChain():void
		{
			var drawedDic:Dictionary = new Dictionary();
			for(var uid:String in _longLoopChainDic)
			{
				var $point:Point = _startedDic[uid];
				if($point==null)continue;
				if(!drawedDic[uid])
				{
					_lineBoard.graphics.lineStyle();
					_lineBoard.graphics.beginFill(0xff0000,1);
					_lineBoard.graphics.drawRoundRect($point.x-POINT_RADIUS*3/2,$point.y-POINT_RADIUS*3/2,POINT_RADIUS*3,POINT_RADIUS*3,10);
					_lineBoard.graphics.endFill();
					drawedDic[uid] = true;
				}
				var $arr:Array = getStartUidsByEndUid(uid);
				for(var i:int=0;i<$arr.length;i++)
				{
					var $preUid:String = $arr[i];
					var $prePoint:Point = _startedDic[$preUid];
					if($prePoint==null)continue;
					if(!drawedDic[$preUid])
					{
						_lineBoard.graphics.lineStyle();
						_lineBoard.graphics.beginFill(0xff0000,1);
						_lineBoard.graphics.drawRoundRect($prePoint.x-POINT_RADIUS*3/2,$prePoint.y-POINT_RADIUS*3/2,POINT_RADIUS*3,POINT_RADIUS*3,10);
						_lineBoard.graphics.endFill();
						drawedDic[$preUid] = true;
					}
					
					_lineBoard.graphics.lineStyle((GRID_HEIGTH+GRID_WIDTH)/2/20,0xff0000,1);
					_lineBoard.graphics.moveTo($point.x,$point.y);
					_lineBoard.graphics.lineTo($prePoint.x,$prePoint.y);
					if($point.x==$prePoint.x && $point.y>$prePoint.y)
					{
						_lineBoard.graphics.lineTo($prePoint.x-GRID_WIDTH/2,$prePoint.y+GRID_HEIGTH/2);
						_lineBoard.graphics.lineTo($point.x-GRID_WIDTH/2,$point.y-GRID_HEIGTH/2);
					}
					else if($point.x==$prePoint.x && $point.y<$prePoint.y)
					{
						_lineBoard.graphics.lineTo($prePoint.x+GRID_WIDTH/2,$prePoint.y-GRID_HEIGTH/2);
						_lineBoard.graphics.lineTo($point.x+GRID_WIDTH/2,$point.y+GRID_HEIGTH/2);
					}
					else if($point.y==$prePoint.y && $point.x>$prePoint.x)
					{
						_lineBoard.graphics.lineTo($prePoint.x+GRID_WIDTH/2,$prePoint.y+GRID_HEIGTH/2);
						_lineBoard.graphics.lineTo($point.x-GRID_WIDTH/2,$point.y+GRID_HEIGTH/2);
					}
					else if($point.y==$prePoint.y && $point.x<$prePoint.x)
					{
						_lineBoard.graphics.lineTo($prePoint.x-GRID_WIDTH/2,$prePoint.y-GRID_HEIGTH/2);
						_lineBoard.graphics.lineTo($point.x+GRID_WIDTH/2,$point.y-GRID_HEIGTH/2);
					}
					else if($point.y<$prePoint.y && $point.x>$prePoint.x)
					{
						_lineBoard.graphics.lineTo($prePoint.x+GRID_WIDTH/2,$prePoint.y-GRID_HEIGTH/2);
						_lineBoard.graphics.lineTo($point.x-GRID_WIDTH/2,$point.y+GRID_HEIGTH/2);
					}
					else if($point.y<$prePoint.y && $point.x<$prePoint.x)
					{
						_lineBoard.graphics.lineTo($prePoint.x-GRID_WIDTH/2,$prePoint.y-GRID_HEIGTH/2);
						_lineBoard.graphics.lineTo($point.x+GRID_WIDTH/2,$point.y+GRID_HEIGTH/2);
					}
					else if($point.y>$prePoint.y && $point.x<$prePoint.x)
					{
						_lineBoard.graphics.lineTo($prePoint.x-GRID_WIDTH/2,$prePoint.y+GRID_HEIGTH/2);
						_lineBoard.graphics.lineTo($point.x+GRID_WIDTH/2,$point.y-GRID_HEIGTH/2);
					}
					else if($point.y>$prePoint.y && $point.x>$prePoint.x)
					{
						_lineBoard.graphics.lineTo($prePoint.x+GRID_WIDTH/2,$prePoint.y+GRID_HEIGTH/2);
						_lineBoard.graphics.lineTo($point.x-GRID_WIDTH/2,$point.y-GRID_HEIGTH/2);
					}
//					_lineBoard.graphics.lineTo($prePoint.x,$prePoint.y+GRID_HEIGTH/2);
//					_lineBoard.graphics.lineTo($point.x,$prePoint.y+GRID_HEIGTH/2);
					
					_lineBoard.graphics.lineTo($point.x,$point.y);
				}
			}
		}
		
		/**
		 *	画单循环链 
		 */
		private function drawSingleLoopChain():void
		{
			
		}
		
		private function drawArrow(startPoint:Point,endPoint:Point):void
		{
			_lineBoard.graphics.lineStyle((GRID_HEIGTH+GRID_WIDTH)/2/10,0x112233,.8);
			_lineBoard.graphics.moveTo(startPoint.x,startPoint.y);
			_lineBoard.graphics.lineTo(endPoint.x,endPoint.y);
			
			_pointBoard.graphics.lineStyle(POINT_RADIUS/10,0x000000);
			_pointBoard.graphics.beginFill(0xff6600,1);
			_pointBoard.graphics.drawCircle(startPoint.x,startPoint.y,POINT_RADIUS);
			_pointBoard.graphics.endFill();
			
			_pointBoard.graphics.beginFill(0x3366ff,1);
			_pointBoard.graphics.drawCircle(endPoint.x,endPoint.y,POINT_RADIUS*1.25);
			_pointBoard.graphics.endFill();
		}
		
		/**
		 *	创建起点坐标 
		 * @param startUid 起点唯一标识
		 * @param isTop 是否是链头
		 * @return 
		 * 
		 */
		private function createStartPoint(startUid:String,isTop:Boolean):Point
		{
			var $point:Point;
			if(_endedDic[startUid]!=null)//如果在终点字典中
			{
				$point = _endedDic[startUid];
				_startedDic[startUid] = $point;
			}
			else if(_startedDic[startUid]!=null)//如果在起点字典中
			{
				$point =  _startedDic[startUid];
			}
			else//否则创建一个新链头
			{
				$point = getNodePoint(0,chainNum);
				_startedDic[startUid] = $point;
				_nodeDic[$point.toString()] = true;
				_headList.push(startUid);
			}
			return $point;
		}
		
		/**
		 *	创建终点坐标 
		 * @param startUid 起点唯一标识
		 * @param endUid 终点唯一标识
		 * @return 
		 * 
		 */
		private function createEndPoint(startUid:String,endUid:String):Point
		{
			var $point:Point;
			
			if(_startedDic[endUid])//终点为某起点,循环链...
			{
				$point = _startedDic[endUid];
				if(startUid==endUid)
				{
					_singleLoopChainDic[endUid] = $point;//单循环链
				}else{
					_longLoopChainDic[endUid] = $point;//长循环链
				}
			}
			else if(_endedDic[endUid])//已在终点字典中
			{
				$point = _endedDic[endUid];
			}
			else//创建一个新终点
			{
				if(_startedDic[startUid]==null)
				{
					throw new Error("error:无法找到对应的起点,创建终点前请先创建与它对应的起点");
				}
				$point = findEmptyPointSurround(_startedDic[startUid]);
				_endedDic[endUid] = $point;
				_nodeDic[$point.toString()] = true;
			}
			return $point;
		}
		
		
		/**
		 *	已给定点为中心在附近寻找一个空闲的点 
		 * @param centerPoint 中心点(屏幕坐标)
		 * @return 
		 * 
		 */
		private function findEmptyPointSurround(centerPoint:Point):Point
		{
			var $cP:Point = centerPoint.clone();
			var $surround:Vector.<Point> = new Vector.<Point>;
			$surround.push($cP.add(getNodePoint(1,0)));//右
			$surround.push($cP.add(getNodePoint(0,1)));//下
			$surround.push($cP.add(getNodePoint(-1,0)));//左
			$surround.push($cP.add(getNodePoint(0,-1)));//上
			$surround.push($cP.add(getNodePoint(1,1)));//右下
			$surround.push($cP.add(getNodePoint(1,-1)));//右上
			$surround.push($cP.add(getNodePoint(-1,-1)));//左上
			$surround.push($cP.add(getNodePoint(-1,1)));//左下
			for(var i:int=0;i<$surround.length;i++)
			{
				if(_nodeDic[$surround[i].toString()]==null)
				{
					return $surround[i];
				}
			}
			return null;
		}

		
		
		/**
		 *	获取网格坐标对应的屏幕坐标
		 * @param x
		 * @param y
		 * @return 
		 * 
		 */
		private function getNodePoint(x:int,y:int):Point
		{
			return new Point(x*GRID_WIDTH,y*GRID_HEIGTH);
		}

		/**
		 *	 返回以给入节点Uid(startUid)为起点的所有后续节点Uid(endUid)
		 * @param startUid
		 * @return 包含此节点所有后续节点Uid的数组[endUid,endUid,....]
		 * 
		 */
		private function getEndUidsByStartUid(startUid:String):Array
		{
			var $arr:Array = [];
			for(var i:int=0;i<_data.length;i++)
			{
				if(startUid==_data[i].start)
				{
					if(_data[i].hasOwnProperty("end") && _data[i].end!=null)
					{
						$arr.push(_data[i].end);
					}
				}
			}
			return $arr;
		}
		
		/**
		 *	 返回以给入节点Uid(endUid)为终点的所有节点Uid(startUid)
		 * @param startUid
		 * @return 包含以此节点为终点的所有节点Uid的数组[startUid,startUid,....]
		 * 
		 */
		private function getStartUidsByEndUid(endUid:String):Array
		{
			var $arr:Array = [];
			for(var i:int=0;i<_data.length;i++)
			{
				if(endUid==_data[i].end)
				{
					if(_data[i].hasOwnProperty("start") && _data[i].start!=null)
					{
						$arr.push(_data[i].start);
					}
				}
			}
			return $arr;
		}
		
		
		/**
		 *	链数 
		 */
		private function get chainNum():int
		{
			return _headList.length;
		}
		
		

//		override public function get width():Number
//		{
//			return getBounds(this).width;
//		}
//
//		override public function get height():Number
//		{
//			return getBounds(this).height;
//		}
	}
}
